package asteroids.statements;


import java.util.*;

import be.kuleuven.cs.som.annotate.Basic;

import asteroids.*;
import asteroids.expressions.*;
import asteroids.model.programs.parsing.ProgramFactory.ForeachType;
import asteroids.types.*;

public class ForEachLoopStatement extends ComposedStatement {

	private ForeachType type;
	private String variableName;
	private Statement body;
	
	public ForEachLoopStatement(int line, int column, ForeachType type, String variableName, Statement body) {
		super(line, column);
		this.type = type;
		this.variableName = variableName;
		this.body = body;
	}

	@Basic
	public String getVariableName() {
		return variableName;
	}

	@Basic
	public ForeachType getType() {
		return type;
	}
	
	@Basic
	public Statement getBody() {
		return body;
	}
	
	@Override
	public int getNbOfStatements() {
		return 1;
	}

	@Override
	public Statement getStatementAt(int index) throws IndexOutOfBoundsException {
		if (index == 1)
			return getBody();
		else
			throw new IndexOutOfBoundsException();
	}
	
	@Override
	public void execute(Ship ship) {
		if(ship.getProgram().getGlobals().containsKey(getVariableName())){
			Facade facade = new Facade();
			if(getType() == ForeachType.SHIP) {
				Set<Ship> ships = facade.getShips(ship.getWorld());
				for(Ship loopShip: ships) {
					(new AssignmentStatement(getLine(), getColumn(), getVariableName(), new EntityLiteral(getLine(), getColumn(),loopShip))).execute(ship);
					getBody().execute(ship);
				}
			}
			else if(getType() == ForeachType.ASTEROID) {
				Set<Asteroid> asteroids = facade.getAsteroids(ship.getWorld());
				for(Asteroid loopAsteroid: asteroids) {
					(new AssignmentStatement(getLine(), getColumn(), getVariableName(), new EntityLiteral(getLine(), getColumn(),loopAsteroid))).execute(ship);
					getBody().execute(ship);
				}
			}
			else if(getType() == ForeachType.BULLET) {
				Set<Bullet> bullets = facade.getBullets(ship.getWorld());
				for(Bullet loopBullet: bullets) {
					(new AssignmentStatement(getLine(), getColumn(), getVariableName(), new EntityLiteral(getLine(), getColumn(),loopBullet))).execute(ship);
					getBody().execute(ship);
				}
			}
			else if(getType() == ForeachType.ANY) {
				for(SpaceEntity loopEntity: ship.getWorld().getSpaceEntities()) {
					(new AssignmentStatement(getLine(), getColumn(), getVariableName(), new EntityLiteral(getLine(), getColumn(),loopEntity))).execute(ship);
					getBody().execute(ship);
				}
			}
		}
	}

	@Override
	public List<String> getTypeCheckErrors(List<String> typeErrors, Map<String, Type> globalVariables) {
		if(getBody().hasActionStatement()){
			typeErrors.add("Line: " + getLine() + " Column: " + getColumn() + " - No action statements allowed in a foreach loop.");
		}
		if(!globalVariables.containsKey(getVariableName())){
			typeErrors.add("Line: " + getLine() + " Column: " + getColumn() + " - There is no global variable with variable name '"+ getVariableName() +"'.");
		}
		typeErrors = getBody().getTypeCheckErrors(typeErrors, globalVariables);
		return typeErrors;
	}

}
